`timescale 1ns/100ps

`include "sim_glb.sv"

module tc;

localparam          U_DLY                   = 0;
localparam          CLK_PRD                 = 5;
localparam          DAT_BW                  = 8;
localparam          DAT_NUM                 = 20000;
localparam          N                       = 8;
localparam          RS_MODE                 = {2'd2, 2'd1, 2'd0, 2'd3,
                                               2'd1, 2'd3, 2'd0, 2'd2};

reg                                         rst_n;
reg                                         clk;

reg                                         gen_vld;
reg                 [DAT_BW-1:0]            gen_dat;
wire                                        gen_rdy;

wire                                        chk_vld;
wire                [DAT_BW-1:0]            chk_dat;
reg                                         chk_rdy;
reg                                         chk_err;

wire                [N:0]                   vld_ary;
wire                [DAT_BW-1:0]            dat_ary[N:0];
wire                [N:0]                   rdy_ary;
wire                [N:0]                   done_ary;

initial begin:CRG
    rst_n=1'b0;
    clk=1'b0;

    fork
        rst_n=#100.5 1'b1;
        forever clk=#CLK_PRD ~clk;
    join
end

RGRS_MNG    rgrs;
initial begin:REGRESS
    rgrs = new("tc_reg_slicer", 2);

    rgrs.wait_chks_done(100_000_000);
end

initial begin:GEN_VLD
    reg     [31:0]      rand_dat;
    integer             i;
    integer             cnt_tx;
    
    cnt_tx  = 0;
    gen_vld = 1'b0;

    @(posedge rst_n);
    #100;

    while (cnt_tx<DAT_NUM) begin
        rand_dat = $urandom();

        for(i=0; i<32; i=i+1) begin
            @(posedge clk);
            if ((gen_vld==1'b1) && (gen_rdy==1'b1)) begin
                cnt_tx = cnt_tx + 1;
            end
            #U_DLY;
            gen_vld = (cnt_tx<DAT_NUM) ? rand_dat[i] : 1'b0;
        end
    end
    rgrs.one_chk_done("tx num is done.");
end

initial begin:GEN_RDY
    reg     [31:0]      rand_dat;
    integer             i;
    integer             cnt_rx;
    
    cnt_rx  = 0;
    chk_rdy = 1'b0;

    @(posedge rst_n);
    #100;

    while (cnt_rx<DAT_NUM) begin
        rand_dat = $urandom();

        for(i=0; i<16; i=i+1) begin
            @(posedge clk);
            if ((chk_vld==1'b1) && (chk_rdy==1'b1)) begin
                cnt_rx = cnt_rx + 1;
            end
            #U_DLY;
            chk_rdy = (cnt_rx<DAT_NUM) ? rand_dat[i] : 1'b0;
        end
    end
    rgrs.one_chk_done("rx num is done.");
end

initial begin:GEN_DAT
    gen_dat = {DAT_BW{1'b0}};

    @(posedge rst_n);

    forever begin
        @(posedge clk);

        if (gen_vld==1'b1 && gen_rdy==1'b1) begin
            gen_dat = gen_dat + 1;
        end
    end
end

assign vld_ary[0] = gen_vld;
assign dat_ary[0] = gen_dat;

assign gen_rdy = rdy_ary[0];

assign done_ary = vld_ary & rdy_ary;

genvar g0;
generate for (g0=0; g0<N; g0=g0+1) begin:G_RS
    
    reg_slicer #(
            .RS_MODE                        (
                                                (RS_MODE[g0*2+:2]==0) ? "VLD_RS_FULL_RATE" :
                                                (RS_MODE[g0*2+:2]==1) ? "RDY_RS_FULL_RATE" :
                                                (RS_MODE[g0*2+:2]==2) ? "BID_RS_FULL_RATE" :
                                                (RS_MODE[g0*2+:2]==3) ? "BID_RS_HALF_RATE" :
                                                                        "BID_RS_FULL_RATE"
                                            ),
            .DAT_BW                         (DAT_BW                         )
    ) U_RS( 
            .rst_n                          (rst_n                          ),
            .clk                            (clk                            ),

            .src_vld                        (vld_ary[g0]                    ),
            .src_dat                        (dat_ary[g0]                    ),
            .src_rdy                        (rdy_ary[g0]                    ),

            .dst_vld                        (vld_ary[g0+1]                  ),
            .dst_dat                        (dat_ary[g0+1]                  ),
            .dst_rdy                        (rdy_ary[g0+1]                  )
    );
    
end endgenerate

assign chk_vld = vld_ary[N];
assign chk_dat = dat_ary[N];

assign rdy_ary[N] = chk_rdy;

initial begin:CHK_DAT
    reg     [DAT_BW-1:0]    old_dat;

    old_dat = {DAT_BW{1'b0}};
    chk_err = 1'b0;

    @(posedge rst_n);
    
    forever begin
        @(posedge clk);

        if (chk_vld==1'b1 && chk_rdy==1'b1) begin
            if (old_dat!=chk_dat) begin
                chk_err = 1'b1;
                $display("%m FAIL:chk_dat is wrong");
                $stop;
            end

            old_dat = chk_dat + 1;
        end
    end
end
endmodule

